package com.sh.data.engine.domain.common.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.base.model.PageResult;
import com.sh.data.engine.domain.base.model.UserContext;
import com.sh.data.engine.domain.base.model.enums.DirectoryMenuEnum;
import com.sh.data.engine.domain.base.model.enums.TagTypeEnum;
import com.sh.data.engine.domain.common.model.domain.TagDomain;
import com.sh.data.engine.domain.common.service.DirectoryService;
import com.sh.data.engine.domain.common.service.TagService;
import com.sh.data.engine.domain.util.ConvertUtil;
import com.sh.data.engine.repository.dao.base.EngineBaseModel;
import com.sh.data.engine.repository.dao.common.TagMapper;
import com.sh.data.engine.repository.dao.common.entity.DirectoryEntity;
import com.sh.data.engine.repository.dao.common.entity.TagEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author : changkong
 * @date : 2023/6/7
 */
@Slf4j
@Service
public class TagServiceImpl extends ServiceImpl<TagMapper, TagEntity> implements TagService {

    @Autowired
    private DirectoryService directoryService;

    @Override
    public Long saveNewTag(TagDomain tagDomain) {
        TagEntity tag = ConvertUtil.copyProperties(tagDomain, TagEntity.class);
        tag.setRowState(1);
        if (!this.save(tag)) {
            throw new BusinessException("新增tag异常");
        }
        return tag.getId();
    }

    @Override
    public void saveTag(TagDomain tagDomain) {
        TagEntity tag = ConvertUtil.copyProperties(tagDomain, TagEntity.class);
        this.updateById(tag);
    }

    /**
     * 叶子节点判断标签下是否有api 父节点判断子标签下是否有api
     *
     * @param id
     */
    @Override
    public void deleteTag(Long projectId, Long id) {
        List<Long> tagBizIdList = getTagBizIdList(projectId, TagTypeEnum.API.getCode(), id);
        if (CollectionUtils.isNotEmpty(tagBizIdList)) {
            throw new BusinessException("该目录或者子目录下存在任务,无法删除");
        }
        log.info("删除id {}的标签", id);
        directoryService.deleteDir(id, DirectoryMenuEnum.API_TAG);
    }

    @Override
    public List<TagDomain> getTagListByApiId(Long projectId, Long id) {
        LambdaQueryWrapper<TagEntity> eq =
            new LambdaQueryWrapper<TagEntity>()
                .eq(TagEntity::getTagType, TagTypeEnum.API.getCode())
                .eq(TagEntity::getRowState, 1)
                .eq(TagEntity::getBizId, id);
        List<TagEntity> list = this.list(eq);
        if (CollectionUtils.isEmpty(list)) return new ArrayList<>();

        List<DirectoryEntity> directoryEntityList =
            directoryService.list(
                new LambdaQueryWrapper<DirectoryEntity>()
                    .in(
                        EngineBaseModel::getId,
                        list.stream().map(TagEntity::getTagId).collect(Collectors.toList())));
        Map<Long, DirectoryEntity> map =
            directoryEntityList.stream()
                .collect(Collectors.toMap(EngineBaseModel::getId, Function.identity()));
        List<TagDomain> result = new ArrayList<>();
        for (TagEntity tag : list) {
            TagDomain tagDomain = new TagDomain();
            Long tagId = tag.getTagId();
            DirectoryEntity directoryEntity = map.get(tagId);
            tagDomain.setTagId(tagId);
            tagDomain.setTagName(
                Optional.ofNullable(directoryEntity).map(DirectoryEntity::getDirName).orElse("默认标签名"));
            tagDomain.setPid(
                Optional.ofNullable(directoryEntity).map(DirectoryEntity::getPid).orElse(null));
            result.add(tagDomain);
        }
        return result;
    }

    /**
     * 编辑api的标签
     *
     * @param apiId
     * @param addTagList
     * @param deleteTagList
     */
    @Override
    public void saveApiTagById(Long apiId, List<Long> addTagList, List<Long> deleteTagList) {
        Long projectId = ServletUtils.getProjectId();
        if (CollectionUtils.isNotEmpty(addTagList)) {

            // 校验addTagList数据
            List<DirectoryEntity> dbTagList =
                directoryService.list(
                    new LambdaQueryWrapper<DirectoryEntity>()
                        .eq(DirectoryEntity::getProjectId, projectId)
                        .in(EngineBaseModel::getId, addTagList));
            List<Long> dbIdList =
                dbTagList.stream().map(EngineBaseModel::getId).collect(Collectors.toList());

            if (!CollectionUtils.isEqualCollection(dbIdList, addTagList)) {
                log.error("新增标签数据异常");
                throw new BusinessException("请检查新增标签是否存在");
            }
            List<TagEntity> list =
                this.list(
                    new LambdaQueryWrapper<TagEntity>()
                        .eq(TagEntity::getProjectId, projectId)
                        .eq(TagEntity::getRowState, 1)
                        .eq(TagEntity::getBizId, apiId));

            // 去重
            Collection<Long> removeDumplicated =
                CollectionUtils.subtract(
                    addTagList, list.stream().map(TagEntity::getTagId).collect(Collectors.toList()));

            if (CollectionUtils.isNotEmpty(removeDumplicated)) {
                List<TagEntity> addList = new ArrayList<>();
                for (Long addId : removeDumplicated) {
                    TagEntity addEntity = new TagEntity();
                    addEntity.setTagId(addId);
                    addEntity.setBizId(apiId);
                    addEntity.setProjectId(projectId);
                    addEntity.setTagType(TagTypeEnum.API.getCode());
                    addEntity.setRowState(1);
                    addEntity.setCreatorId(UserContext.getUserId());
                    addEntity.setUpdaterId(UserContext.getUserId());
                    addList.add(addEntity);
                }
                this.saveBatch(addList);
            }
        }

        if (CollectionUtils.isNotEmpty(deleteTagList)) {
            this.update(
                new LambdaUpdateWrapper<TagEntity>()
                    .set(TagEntity::getUpdaterId, UserContext.getUserId())
                    .set(TagEntity::getRowState, 0)
                    .eq(TagEntity::getProjectId, projectId)
                    .eq(TagEntity::getBizId, apiId)
                    .in(TagEntity::getTagId, deleteTagList));
        }
    }

    @Override
    public List<Long> getTagBizIdList(Long projectId, Integer tagType, Long id) {
        LambdaQueryWrapper<DirectoryEntity> listQuery =
            new LambdaQueryWrapper<DirectoryEntity>()
                .eq(DirectoryEntity::getMenuCode, DirectoryMenuEnum.API_TAG.getCode())
                .eq(DirectoryEntity::getProjectId, projectId);

        List<DirectoryEntity> directoryEntityList = directoryService.list(listQuery);
        Map<Long, List<DirectoryEntity>> map =
            directoryEntityList.stream().collect(Collectors.groupingBy(DirectoryEntity::getPid));

        Queue<Long> q = new LinkedList();
        List<Long> directoryIdList = new ArrayList<>();
        q.add(id);
        while (!q.isEmpty()) {
            Long qId = q.remove();
            directoryIdList.add(qId);
            List<DirectoryEntity> directoryEntities = map.get(qId);
            if (CollectionUtils.isNotEmpty(directoryEntities)) {
                q.addAll(directoryEntities.stream().map(EngineBaseModel::getId).collect(Collectors.toList()));
            }
        }

        LambdaQueryWrapper<TagEntity> eq =
            new LambdaQueryWrapper<TagEntity>()
                .eq(TagEntity::getProjectId, projectId)
                .eq(TagEntity::getTagType, tagType)
                .eq(TagEntity::getRowState, 1)
                .in(TagEntity::getTagId, directoryIdList);
        List<TagEntity> list = this.list(eq);

        // 去重, 一个api可能同时在父子标签下
        TreeSet<TagEntity> treeSet =
            new TreeSet(
                (Comparator<TagEntity>)
                    (o1, o2) -> DateUtil.compare(o1.getUpdateTime(), o2.getUpdateTime()));
        treeSet.addAll(list);
        return treeSet.stream().map(TagEntity::getBizId).collect(Collectors.toList());
    }

    @Override
    public Collection<Long> getTagBizIdPage(
        Long projectId, Integer tagType, Long id, Integer pageNum, Integer pageSize) {
        PageResult pageResult =
            new PageResult(getTagBizIdList(projectId, tagType, id), pageNum, pageSize);
        return pageResult.getResult();
    }
}
